home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1995 #5 & #6 / Amiga Plus CD - 1995 - No. 5 and 6.iso / pd / texte / amigafaq / bin / texi2html
Text File  |  1994-12-05  |  42KB  |  1,591 lines

  1. #!/usr/local/dist/bin/perl4
  2. 'di';
  3. 'ig00';
  4. #+##############################################################################
  5. #                                                                              #
  6. # File: texi2html                                                              #
  7. #                                                                              #
  8. # Description: Program to transform most Texinfo documents to HTML             #
  9. #                                                                              #
  10. #-##############################################################################
  11.  
  12. # @(#)texi2html    1.29 04/21/94    Written by Lionel Cons, Lionel.Cons@cern.ch
  13.  
  14. # texi2html 1.29af 08.08.94 & 01.12.94   by Hans Maurer
  15. # Made some modifications for better compatability with AmigaFAQ
  16.  
  17. # The man page for this program is included at the end of this file and can be
  18. # viewed using the command 'nroff -man texi2html'.
  19. # Please read the copyright at the end of the man page.
  20.  
  21. #+++############################################################################
  22. #                                                                              #
  23. # Constants                                                                    #
  24. #                                                                              #
  25. #---############################################################################
  26.  
  27. $DEBUG_TOC = 1;
  28. $DEBUG_INDEX = 2;
  29. $DEBUG_BIB = 4;
  30. $DEBUG_GLOSS = 8;
  31. $DEBUG_DEF = 16;
  32.  
  33. $BIBRE = '\[[\w\/]+\]';            # RE for a bibliography reference
  34. $FILERE = '[\/\w.+-]+';            # RE for a file name
  35. $VARRE = '[^\s\{\}]+';            # RE for a variable name
  36. $NODERE = '[^@{}:\'`",]+';        # RE for a node name
  37. $NODESRE = '[^@{}:\'`"]+';        # RE for a list of node names
  38. $XREFRE = '[^@{}]+';            # RE for a xref (should use NODERE)
  39.  
  40. $THISPROG = "texi2html 1.29af";            # program name and version
  41. $TODAY = &pretty_date;            # like "20 September 1993"
  42. $SPLITTAG = "<!-- SPLIT HERE -->\n";    # tag to know where to split
  43. $PROTECTTAG = "_ThisIsProtected_";    # tag to recognize protected sections
  44.  
  45. #
  46. # language dependent constants
  47. #
  48. %LDC =
  49.     (
  50.      'xrefstring',     'see',
  51.      'Xrefstring',     'See',
  52.      'Footnotestring', 'Footnote',
  53.      'Chapterstring',  'Chapter',
  54.      'Appendixstring', 'Appendix',
  55.      'Sectionstring',  'Section',
  56.      'sectionstring',  'section',
  57.      'pagestring',     'page',
  58.      );
  59.  
  60. #
  61. # iso-8859-1 representations
  62. #
  63. %iso =
  64.     (
  65.      "\xe4", 'ä',
  66.      "\xf6", 'ö',
  67.      "\xfc", 'ü',
  68.      "\xc4", 'Ä',
  69.      "\xd6", 'Ö',
  70.      "\xdc", 'Ü',
  71.      "\xdf", 'ß'
  72.      );
  73.  
  74. #
  75. # texinfo section names to level
  76. #
  77. %sec2level = (
  78.           'top', 0,
  79.           'chapter', 1,
  80.           'unnumbered', 1,
  81.           'majorheading', 1,
  82.           'chapheading', 1,
  83.           'appendix', 1,
  84.           'section', 2,
  85.           'unnumberedsec', 2,
  86.           'heading', 2,
  87.           'appendixsec', 2,
  88.           'appendixsection', 2,
  89.           'subsection', 3,
  90.           'unnumberedsubsec', 3,
  91.           'subheading', 3,
  92.           'appendixsubsec', 3,
  93.           'subsubsection', 4,
  94.           'unnumberedsubsubsec', 4,
  95.           'subsubheading', 4,
  96.           'appendixsubsubsec', 4,
  97.           );
  98.  
  99. #
  100. # texinfo "simple things" (@foo) to HTML ones
  101. #
  102. %simple_map = (
  103.            # cf. makeinfo.c
  104.            "*", "<BR>",        # HTML+
  105.            " ", " ",
  106.            "\n", "\n",
  107.            "|", "",
  108.            # spacing commands
  109.            ":", "",
  110.            "!", "!",
  111.            "?", "?",
  112.            ".", ".",
  113.            );
  114.  
  115. #
  116. # texinfo "things" (@foo{}) to HTML ones
  117. #
  118. %things_map = (
  119.            'TeX', 'TeX',
  120.            'br', '<P>',        # paragraph break
  121.            'bullet', '*',
  122.            'copyright', '(C)',
  123.            'dots', '...',
  124.            'equiv', '==',
  125.            'error', 'error-->',
  126.            'expansion', '==>',
  127.            'minus', '-',
  128.            'point', '-!-',
  129.            'print', '-|',
  130.            'result', '=>',
  131.            'today', $TODAY,
  132.            );
  133.  
  134. #
  135. # texinfo styles (@foo{bar}) to HTML ones
  136. #
  137. %style_map = (
  138.           'asis', '',
  139.           'b', 'B',
  140.           'cite', 'CITE',
  141.           'code', 'CODE',
  142.           'ctrl', '&do_ctrl',    # special case
  143.           'dfn', 'DFN',
  144.           'dmn', '',        # useless
  145.           'emph', 'EM',
  146.           'file', '"TT',        # will put quotes, cf. &apply_style
  147.           'i', 'I',
  148.           'kbd', 'KBD',
  149.           'key', 'KBD',
  150.           'r', '',            # unsupported
  151.           'samp', '"SAMP',        # will put quotes, cf. &apply_style
  152.           'sc', '&do_sc',        # special case
  153.           'subtitle', 'H2',
  154.           'strong', 'STRONG',
  155.           't', 'TT',
  156.           'title', 'H1',
  157.           'titlefont', '',        # useless
  158.           'var', 'VAR',
  159.           'w', '',            # unsupported
  160.           );
  161.  
  162. #
  163. # texinfo format (@foo/@end foo) to HTML ones
  164. #
  165. %format_map = (
  166.            'display', 'PRE',
  167.            'example', 'PRE',
  168.            'format', 'PRE',
  169.            'lisp', 'PRE',
  170.            'quotation', 'BLOCKQUOTE',
  171.            'smallexample', 'PRE',
  172.            'smalllisp', 'PRE',
  173.            # lists
  174.            'itemize', 'UL',
  175.            'enumerate', 'OL',
  176.            # poorly supported
  177.            'flushleft', 'PRE',
  178.            'flushright', 'PRE',
  179.            );
  180.  
  181. #
  182. # texinfo definition shortcuts to real ones
  183. #
  184. %def_map = (
  185.         # basic commands
  186.         'deffn', 0,
  187.         'defvr', 0,
  188.         'deftypefn', 0,
  189.         'deftypevr', 0,
  190.         'defcv', 0,
  191.         'defop', 0,
  192.         'deftp', 0,
  193.         # basic x commands
  194.         'deffnx', 0,
  195.         'defvrx', 0,
  196.         'deftypefnx', 0,
  197.         'deftypevrx', 0,
  198.         'defcvx', 0,
  199.         'defopx', 0,
  200.         'deftpx', 0,
  201.         # shortcuts
  202.         'defun', 'deffn Function',
  203.         'defmac', 'deffn Macro',
  204.         'defspec', 'deffn {Special Form}',
  205.         'defvar', 'defvr Variable',
  206.         'defopt', 'defvr {User Option}',
  207.         'deftypefun', 'deftypefn Function',
  208.         'deftypevar', 'deftypevr Variable',
  209.         'defivar', 'defcv {Instance Variable}',
  210.         'defmethod', 'defop Method',
  211.         # x shortcuts
  212.         'defunx', 'deffnx Function',
  213.         'defmacx', 'deffnx Macro',
  214.         'defspecx', 'deffnx {Special Form}',
  215.         'defvarx', 'defvrx Variable',
  216.         'defoptx', 'defvrx {User Option}',
  217.         'deftypefunx', 'deftypefnx Function',
  218.         'deftypevarx', 'deftypevrx Variable',
  219.         'defivarx', 'defcvx {Instance Variable}',
  220.         'defmethodx', 'defopx Method',
  221.         );
  222.  
  223. #
  224. # things to skip
  225. #
  226. %to_skip = (
  227.         # comments
  228.         'c', 1,
  229.         'comment', 1,
  230.         # useless
  231.         'contents', 1,
  232.         'shortcontents', 1,
  233.         'summarycontents', 1,
  234.         'footnotestyle', 1,
  235.         'end ifclear', 1,
  236.         'end ifset', 1,
  237.         'iftex', 1,
  238.         'end iftex', 1,
  239.         'parskip', 1,
  240.         'titlepage', 1,
  241.         'end titlepage', 1,
  242.         # unsupported commands (formatting)
  243.         'afourpaper', 1,
  244.         'cropmarks', 1,
  245.         'finalout', 1,
  246.         'headings', 1,
  247.         'need', 1,
  248.         'page', 1,
  249.         'setchapternewpage', 1,
  250.         'everyheading', 1,
  251.         'everyfooting', 1,
  252.         'evenheading', 1,
  253.         'evenfooting', 1,
  254.         'oddheading', 1,
  255.         'oddfooting', 1,
  256.         'smallbook', 1,
  257.         'vskip', 1,
  258.         # unsupported formats
  259.         'cartouche', 1,
  260.         'end cartouche', 1,
  261.         'group', 1,
  262.         'end group', 1,
  263.         # misc unsupported commands
  264.             'defindex', 1,
  265.         );
  266.  
  267. #+++############################################################################
  268. #                                                                              #
  269. # Argument parsing, initialisation                                             #
  270. #                                                                              #
  271. #---############################################################################
  272.  
  273. $invisible_mark = '';
  274. $use_bibliography = 1;
  275. $usage = <<EOT;
  276. This is $THISPROG
  277. To convert a Texinfo file to HMTL: $0 [options] file
  278.   where options can be:
  279.     -glossary      : handle a glossary
  280.     -invisible name: use name as an invisible anchor
  281.     -menu          : handle menus
  282.     -split_chapter : split on main sections
  283.     -split_node    : split on nodes
  284.     -usage         : print usage instructions
  285.     -verbose       : verbose output
  286. To check converted files: $0 -check [-verbose] files
  287. EOT
  288.  
  289. while ($_ = $ARGV[0], /^-/) {
  290.     shift;
  291.     if (/^-d(ebug)?(\d+)?$/) { $debug = $2 || shift; next; }
  292.     if (/^-c(heck)?$/)       { $check = 1; next; }
  293.     if (/^-g(lossary)?$/)    { $use_glossary = 1; next; }
  294.     if (/^-i(nvisible)?$/)   { $invisible_mark = shift; next; }
  295.     if (/^-iso$/)            { $use_iso = 1; next; }
  296.     if (/^-m(enu)?$/)        { $show_menu = 1; next; }
  297.     if (/^-s(plit)?_?(n(ode)?|c(hapter)?)?$/) {
  298.     if ($2 =~ /^n/) {
  299.         $split_node = 1;
  300.     } else {
  301.         $split_chapter = 1;
  302.     }
  303.     next;
  304.     }
  305.     if (/^-v(erbose)?$/)     { $verbose = 1; next; }
  306.     die $usage;
  307. }
  308.  
  309. if ($check) {
  310.     die $usage unless @ARGV > 0;
  311.     ✓
  312.     exit;
  313. }
  314.  
  315. $invisible_mark = '<IMG SRC="invisible.xbm">' if $invisible_mark eq 'xbm';
  316. die $usage unless @ARGV == 1;
  317. $docu = shift(@ARGV);
  318. if ($docu =~ /.*\//) {
  319.     chop($docu_dir = $&);
  320.     $docu_name = $';
  321. } else {
  322.     $docu_dir = '.';
  323.     $docu_name = $docu;
  324. }
  325. $docu_name =~ s/\.te?x(i|info)?$//;    # basename of the document
  326.  
  327. $docu_toc = $docu_doc = $docu_foot = $docu_name;
  328. $docu_toc  .= '_toc.html';        # document's table of contents
  329. $docu_doc  .= '.html';            # document's contents
  330. $docu_foot .= '_foot.html';        # document's footnotes
  331.  
  332. #
  333. # variables
  334. #
  335. %value = ();                # hold texinfo variables
  336. $value{'html'} = 1;            # predefine html (the output format)
  337. $value{'texi2html'} = '1.29';        # predefine texi2html (the translator)
  338. %node2sec = ();                # node to section name
  339. %node2href = ();            # node to HREF
  340. %bib2href = ();                # bibliography reference to HREF
  341. %gloss2href = ();            # glossary term to HREF
  342. @sections = ();                # list of sections
  343. %tag2pro = ();                # protected sections
  344.  
  345. #
  346. # initial indexes
  347. #
  348. $bib_num = 0;
  349. $foot_num = 0;
  350. $gloss_num = 0;
  351. $idx_num = 0;
  352. $sec_num = 0;
  353. $doc_num = 0;
  354. $html_num = 0;
  355.  
  356. #
  357. # can I use ISO8879 characters? (HTML+)
  358. #
  359. if ($use_iso) {
  360.     $things_map{'bullet'} = "•";
  361.     $things_map{'copyright'} = "©";
  362.     $things_map{'dots'} = "…";
  363.     $things_map{'equiv'} = "≡";
  364.     $things_map{'expansion'} = "→";
  365.     $things_map{'point'} = "∗";
  366.     $things_map{'result'} = "⇒";
  367. }
  368.  
  369. #
  370. # read texi2html extensions (if any)
  371. #
  372. $extensions = 'texi2html.ext'; # extensions in working directory
  373. if (-f $extensions) {
  374.     print "# reading extensions from $extensions\n" if $verbose;
  375.     require($extensions);
  376. }
  377. ($progdir = $0) =~ s/[^\/]+$//;
  378. if ($progdir && ($progdir ne './')) {
  379.     $extensions = "${progdir}texi2html.ext"; # extensions in texi2html directory
  380.     if (-f $extensions) {
  381.     print "# reading extensions from $extensions\n" if $verbose;
  382.     require($extensions);
  383.     }
  384. }
  385.  
  386. print "# reading from $docu\n" if $verbose;
  387.  
  388. #+++############################################################################
  389. #                                                                              #
  390. # Pass 1: read source, handle command, variable, simple substitution           #
  391. #                                                                              #
  392. #---############################################################################
  393.  
  394. @lines = ();                # whole document
  395. @toc_lines = ();            # table of contents
  396. $curlevel = 0;                # current level in TOC
  397. $node = '';                # current node name
  398. $in_table = 0;                # am I inside a table
  399. $table_type = '';            # type of table ('', 'f', 'v')
  400. $in_bibliography = 0;            # am I inside a bibliography
  401. $in_glossary = 0;            # am I inside a glossary
  402. $in_top = 0;                # am I inside the top node
  403. $in_pre = 0;                # am I inside a preformatted section
  404. $in_html = 0;                # am I inside an HTML section
  405. $first_line = 1;                # is it the first line
  406. $dont_html = 0;                # don't protect HTML on this line
  407. $split_num = 0;                # split index
  408.  
  409. # build code for simple substitutions
  410. # the maps used (%simple_map and %things_map) MUST be aware of this
  411. # watch out for regexps, / and escaped characters!
  412. $subst_code = '';
  413. for (keys(%simple_map)) {
  414.     ($re = $_) =~ s/(\W)/\\$1/g; # protect regexp chars
  415.     $subst_code .= "s/\\@$re/$simple_map{$_}/g && study;\n";
  416. }
  417. for (keys(%things_map)) {
  418.     $subst_code .= "s/\\@$_\\{\\}/$things_map{$_}/g && study;\n";
  419. }
  420.  
  421. &init_input;
  422. while ($_ = &next_line) {
  423.     #
  424.     # remove \input or % on the first lines
  425.     #
  426.     if ($first_line && /^(\\input|%)/) {
  427.     next;
  428.     }
  429.     else {
  430.     $first_line = 0;
  431.     }
  432.     #
  433.     # parse texinfo tags
  434.     #
  435.     $tag = '';
  436.     $end_tag = '';
  437.     if (/^\@end\s+(\w+)\b/) {
  438.     $end_tag = $1;
  439.     } elsif (/^\@(\w+)\b/) {
  440.     $tag = $1;
  441.     }
  442.     #
  443.     # handle @ifhtml / @end ifhtml
  444.     #
  445.     if ($in_html) {
  446.     if ($end_tag eq 'ifhtml') {
  447.         $in_html = 0;
  448.     } else {
  449.         $tag2pro{$in_html} .= $_;
  450.     }
  451.     next;
  452.     } elsif ($tag eq 'ifhtml') {
  453.     $in_html = $PROTECTTAG . ++$html_num;
  454.     push(@lines, $in_html);
  455.     next;
  456.     }
  457.     #
  458.     # try to skip the line
  459.     #
  460.     if ($end_tag) {
  461.     next if $to_skip{"end $end_tag"};
  462.     } elsif ($tag) {
  463.     next if $to_skip{$tag};
  464.     last if $tag eq 'bye';
  465.     }
  466.     if ($in_top) {
  467.     # parsing the top node
  468.     if ($tag eq 'node' || $tag eq 'include' || $sec2level{$tag}) {
  469.         # no more in top
  470.         $in_top = 0;
  471.     } else {
  472.         # skip it
  473.         next;
  474.     }
  475.     }
  476.     #
  477.     # try to remove inlined comments
  478.     # syntax from tex-mode.el comment-start-skip
  479.     #
  480.     s/((^|[^\@])(\@\@)*)\@c(omment)? .*/\1/;
  481.     #
  482.     # analyze the tag
  483.     #
  484.     if ($tag) {
  485.     # skip lines
  486.     &skip_until($tag), next if $tag eq 'ignore';
  487.     &skip_until($tag), next if $tag eq 'ifinfo';
  488.     &skip_until($tag), next if $tag eq 'tex';
  489.     # handle special tables
  490.     if ($tag eq 'table') {
  491.         $table_type = '';
  492.     } elsif ($tag eq 'ftable') {
  493.         $tag = 'table';
  494.         $table_type = 'f';
  495.     } elsif ($tag eq 'vtable') {
  496.         $tag = 'table';
  497.         $table_type = 'v';
  498.     }
  499.     # special cases
  500.     if ($tag eq 'top' || ($tag eq 'node' && /^\@node\s+top\s*,/i)) {
  501.         $in_top = 1;
  502.         @lines = (); # ignore all lines before top (title page garbage)
  503.         next;
  504.     } elsif ($tag eq 'node') {
  505.         $in_top = 0;
  506.         &protect_html;    # if node contains '&' for instance
  507.         warn "Bad node line: $_" unless $_ =~ /^\@node\s$NODESRE$/o;
  508.         s/^\@node\s+//;
  509.         ($node) = split(/,/);
  510.         $node =~ s/\s+/ /g; # normalize
  511.         $node =~ s/ $//;
  512.         if ($split_node) {
  513.         &next_doc;
  514.         push(@lines, $SPLITTAG) if $split_num++;
  515.         push(@sections, $node);
  516.         }
  517.         next;
  518.     } elsif ($tag eq 'include') {
  519.         if (/^\@include\s+($FILERE)\s*$/o) {
  520.         $file = $1;
  521.         $file = "$docu_dir/$file" unless -e $file;
  522.         if (-e $file) {
  523.             &open($file);
  524.             print "# including $file\n" if $verbose;
  525.         } else {
  526.             warn "Can't find $file, skipping";
  527.         }
  528.         } else {
  529.         warn "Bad include line: $_";
  530.         }
  531.         next;
  532.     } elsif ($tag eq 'ifclear') {
  533.         if (/^\@ifclear\s+($VARRE)\s*$/o) {
  534.         next unless defined($value{$1});
  535.         &skip_until($tag);
  536.         } else {
  537.         warn "Bad ifclear line: $_";
  538.         }
  539.         next;
  540.     } elsif ($tag eq 'ifset') {
  541.         if (/^\@ifset\s+($VARRE)\s*$/o) {
  542.         next if defined($value{$1});
  543.         &skip_until($tag);
  544.         } else {
  545.         warn "Bad ifset line: $_";
  546.         }
  547.         next;
  548.     } elsif ($tag eq 'menu' && ! $show_menu) {
  549.         &skip_until($tag);
  550.         next;
  551.     } elsif ($format_map{$tag}) {
  552.         $in_pre = 1 if $format_map{$tag} eq 'PRE';
  553.         push(@lines, "<$format_map{$tag}>\n");
  554.         next;
  555.     } elsif ($tag eq 'table') {
  556.         if (/^\@[fv]?table\s+\@(\w+)\s*$/) {
  557.         $in_table = $1;
  558.         push(@lines, "<DL COMPACT>\n");
  559.         } else {
  560.         warn "Bad table line: $_";
  561.         }
  562.         next;
  563.     } elsif ($tag eq 'synindex' || $tag eq 'syncodeindex') {
  564.         if (/^\@$tag\s+(\w)\w\s+(\w)\w\s*$/) {
  565.         eval("*${1}index = *${2}index");
  566.         } else {
  567.         warn "Bad syn*index line: $_";
  568.         }
  569.         next;
  570.     } elsif ($tag eq 'sp') {
  571.         push(@lines, "<P>\n");
  572.         next;
  573.     } elsif (defined($def_map{$tag})) {
  574.         if ($def_map{$tag}) {
  575.         s/^\@$tag\s+//;
  576.         $tag = $def_map{$tag};
  577.         $_ = "\@$tag $_";
  578.         $tag =~ s/\s.*//;
  579.         }
  580.     }
  581.     if (defined($def_map{$tag})) {
  582.         s/^\@$tag\s+//;
  583.         $tag =~ s/x$//;
  584.         1 while s/(\{[^\}]*)\s+([^\{]*\})/$1$;9$2/; # protect spaces inside {}
  585.         @args = split(/\s+/, $_);
  586.         for (@args) {s/$;9/ /g;} # unprotect spaces
  587.         $type = shift(@args);
  588.         $type =~ s/^\{(.*)\}$/$1/;
  589.         print "# def ($tag): {$type} ", join(', ', @args), "\n"
  590.         if $debug & $DEBUG_DEF;
  591.         $type .= ':'; # it's nicer like this
  592.         $name = shift(@args);
  593.         $name =~ s/^\{(.*)\}$/$1/;
  594.         if ($tag eq 'deffn' || $tag eq 'defvr' || $tag eq 'deftp') {
  595.         $_ = "<U>$type</U> <B>$name</B>";
  596.         $_ .= " <I>@args</I>" if @args;
  597.         $_ .= "<P>\n";
  598.         } elsif ($tag eq 'deftypefn' || $tag eq 'deftypevr'
  599.              || $tag eq 'defcv' || $tag eq 'defop') {
  600.         $ftype = $name;
  601.         $name = shift(@args);
  602.         $name =~ s/^\{(.*)\}$/$1/;
  603.         $_ = "<U>$type</U> $ftype <B>$name</B>";
  604.         $_ .= " <I>@args</I>" if @args;
  605.         $_ .= "<P>\n";
  606.         } else {
  607.         warn "Unknown definition type: $tag\n";
  608.         $_ = "<U>$type</U> <B>$name</B>";
  609.         $_ .= " <I>@args</I>" if @args;
  610.         $_ .= "<P>\n";
  611.         }
  612.         if ($tag eq 'deffn' || $tag eq 'deftypefn' || $tag eq 'defop') {
  613.         unshift(@input_spool, "\@findex $name\n");
  614.         } elsif ($tag eq 'defvr' || $tag eq 'deftypevr' || $tag eq 'defcv') {
  615.         unshift(@input_spool, "\@vindex $name\n");
  616.         } else {
  617.         unshift(@input_spool, "\@tindex $name\n");
  618.         }
  619.         $dont_html = 1;
  620.     }
  621.     } elsif ($end_tag) {
  622.     if ($format_map{$end_tag}) {
  623.         $in_pre = 0 if $format_map{$end_tag} eq 'PRE';
  624.         push(@lines, "</$format_map{$end_tag}>\n");
  625.     } elsif ($end_tag eq 'table' ||
  626.          $end_tag eq 'ftable' ||
  627.          $end_tag eq 'vtable') {
  628.         $in_table = 0;
  629.         push(@lines, "</DL>\n");
  630.     } elsif (defined($def_map{$end_tag})) {
  631.         push(@lines, "<P>\n");
  632.     } elsif ($end_tag eq 'menu') {
  633.         push(@lines, $_); # must keep it for pass 2
  634.     }
  635.     next;
  636.     }
  637.     #
  638.     # misc things
  639.     #
  640.     # protect texi and HTML things
  641.     &protect_texi;
  642.     &protect_html unless $dont_html;
  643.     $dont_html = 0;
  644.     # non-@ substitutions cf. texinfmt.el
  645.     s/``/"/g && study;
  646.     s/''/"/g && study;
  647.     s/([\w ])---([\w ])/$1--$2/g && study;
  648.     # substitution (unsupported things)
  649.     s/^\@center\s+//g && study;
  650.     s/^\@exdent\s+//g && study;
  651.     s/\@noindent\s+//g && study;
  652.     s/\@refill\s+//g && study;
  653.     # other substitutions
  654.     eval($subst_code);
  655.     s/\@value{($VARRE)}/$value{$1}/eg;
  656.     s/\@footnote\{/\@footnote$docu_doc\{/g; # mark footnotes, cf. pass 4
  657.     #
  658.     # analyze the tag again
  659.     #
  660.     if ($tag) {
  661.     if ($sec2level{$tag} > 0) {
  662.         if (/^\@$tag\s+(.+)$/) {
  663.         $name = $1;
  664.         $name =~ s/\s+$//;
  665.         $level = $sec2level{$tag};
  666.         if ($tag =~ /heading$/) {
  667.             $_ = "<H$level>$name</H$level>\n";
  668.             print "# heading, section $name, level $level\n"
  669.             if $debug & $DEBUG_TOC;
  670.         } else {
  671.             if ($split_chapter && $level == 1) {
  672.             &next_doc;
  673.             push(@lines, $SPLITTAG) if $split_num++;
  674.             push(@sections, $name);
  675.             }
  676.             $id = 'SEC' . ++$sec_num;
  677.             # check biblio and glossary
  678.             $in_bibliography = ($name =~ /^bibliography$/i);
  679.             $in_glossary = ($name =~ /^glossary$/i);
  680.             # check node
  681.             if ($node) {
  682.             if ($node2sec{$node}) {
  683.                 warn "Duplicate node found: $node\n";
  684.             } else {
  685.                 $node2sec{$node} = $name;
  686.                 $node2href{$node} = "$docu_doc#$id";
  687.                 print "# node $node, section $name, level $level\n"
  688.                 if $debug & $DEBUG_TOC;
  689.             }
  690.             $node = '';
  691.             } else {
  692.             print "# no node, section $name, level $level\n"
  693.                 if $debug & $DEBUG_TOC;
  694.             }
  695.             # update TOC
  696.             while ($level > $curlevel) {
  697.             $curlevel++;
  698.             push(@toc_lines, "<UL>\n");
  699.             }
  700.             while ($level < $curlevel) {
  701.             $curlevel--;
  702.             push(@toc_lines, "</UL>\n");
  703.             }
  704.             $_ = "<LI>" . &anchor($id, "$docu_doc#$id", $name, 1);
  705.             push(@toc_lines, &substitute_style($_));
  706.             # update DOC
  707.             $_ =  "<H$level>" . &anchor($id, "$docu_toc#$id", $name) . "</H$level>\n";
  708.         }
  709.         # update DOC
  710.         push(@lines, $_);
  711.         next;
  712.         } else {
  713.         warn "Bad section line: $_";
  714.         }
  715.     } else {
  716.         # track variables
  717.         $value{$1} = $2, next if /^\@set\s+($VARRE)\s+(.*)$/o;
  718.         delete $value{$1}, next if /^\@clear\s+($VARRE)\s*$/o;
  719.         # store things
  720.         $value{'filename'} = $1, next if /^\@setfilename\s+(.*)$/;
  721.         $value{'author'} .= "$1\n", next if /^\@author\s+(.*)$/;
  722.         $value{'subtitle'} .= "$1\n", next if /^\@subtitle\s+(.*)$/;
  723.         $value{'title'} = $1, next if /^\@settitle\s+(.*)$/;
  724.         $value{'title'} = $1, next if /^\@title\s+(.*)$/;
  725.         # index
  726.         if (/^\@(.index)\s+/) {
  727.         $id = 'IDX' . ++$idx_num;
  728.         $index = $1;
  729.         $what = &substitute_style($');
  730.         $what =~ s/\s+$//;
  731.         print "# found $index for '$what' id $id\n"
  732.             if $debug & $DEBUG_INDEX;
  733.         eval("\$$index\{\$what\} = \"$docu_doc#$id\"");
  734.         # check last line
  735.         if ($lines[$#lines] =~ /^\</) {
  736.             # we can safely (?) insert the index before the last line
  737.             splice(@lines, -1, 0, &anchor($id, '', $invisible_mark, 1));
  738.         } else {
  739.             # it's safer to append the index
  740.             push(@lines, &anchor($id, '', $invisible_mark, !$in_pre));
  741.         }
  742.         next;
  743.         }
  744.         # list item
  745.         if (/^\@itemx?\s+/) {
  746.         $what = $';
  747.         $what =~ s/\s+$//;
  748.         if ($in_bibliography && $use_bibliography) {
  749.             if ($what =~ /^$BIBRE$/o) {
  750.             $id = 'BIB' . ++$bib_num;
  751.             $bib2href{$what} = "$docu_doc#$id";
  752.             print "# found bibliography for '$what' id $id\n"
  753.                 if $debug & $DEBUG_BIB;
  754.             $what = &anchor($id, '', $what);
  755.             }
  756.         } elsif ($in_glossary && $use_glossary) {
  757.             $id = 'GLOSS' . ++$gloss_num;
  758.             $entry = $what;
  759.             $entry =~ tr/A-Z/a-z/ unless $entry =~ /^[A-Z\s]+$/;
  760.             $gloss2href{$entry} = "$docu_doc#$id";
  761.             print "# found glossary for '$entry' id $id\n"
  762.             if $debug & $DEBUG_GLOSS;
  763.             $what = &anchor($id, '', $what);
  764.         }
  765.         if ($in_table) {
  766.             if ($things_map{$in_table} && !$what) {
  767.             # special case to allow @table @bullet for instance
  768.             push(@lines, "<DT>$things_map{$in_table}\n<DD>");
  769.             } else {
  770.             push(@lines, "<DT>\@$in_table{$what}\n<DD>");
  771.             }
  772.             if ($table_type) { # add also an index
  773.             unshift(@input_spool, "\@${table_type}index $what\n");
  774.             }
  775.         } else {
  776.             push(@lines, "<LI>$what\n");
  777.         }
  778.         next;
  779.         }
  780.     }
  781.     }
  782.     # paragraph separator
  783.     $_ = "<P>\n" if $_ eq "\n" && ! $in_pre;
  784.     # otherwise
  785.     push(@lines, $_);
  786. }
  787.  
  788. # finish TOC
  789. $level = 0;
  790. while ($level < $curlevel) {
  791.     $curlevel--;
  792.     push(@toc_lines, "</UL>\n");
  793. }
  794.  
  795. print "# end of pass 1\n" if $verbose;
  796.  
  797. #+++############################################################################
  798. #                                                                              #
  799. # Pass 2/3: handle style, menu, index, cross-reference                         #
  800. #                                                                              #
  801. #---############################################################################
  802.  
  803. @lines2 = ();                # whole document (2nd pass)
  804. @lines3 = ();                # whole document (3rd pass)
  805. $in_menu = 0;                # am I inside a menu
  806.  
  807. while (@lines) {
  808.     $_ = shift(@lines);
  809.     #
  810.     # special case (protected sections)
  811.     #
  812.     if (/^$PROTECTTAG/o) {
  813.     push(@lines2, $_);
  814.     next;
  815.     }
  816.     #
  817.     # menu
  818.     #
  819.     $in_menu = 1, push(@lines2, "<UL>\n"), next if /^\@menu\b/;
  820.     $in_menu = 0, push(@lines2, "</UL>\n"), next if /^\@end\s+menu\b/;
  821.     if ($in_menu) {
  822.     if (/^\*\s+($NODERE)::/o) {
  823.         $descr = $';
  824.         chop($descr);
  825.         &menu_entry($1, $1, $descr);
  826.     } elsif (/^\*\s+(.+):\s+([^\t,\.\n]+)[\t,\.\n]/) {
  827.         $descr = $';
  828.         chop($descr);
  829.         &menu_entry($1, $2, $descr);
  830.     } elsif (/^\*/) {
  831.         warn "Bad menu line: $_";
  832.     } else { # description continued?
  833.         push(@lines2, $_);
  834.     }
  835.     next;
  836.     }
  837.     #
  838.     # printindex
  839.     #
  840.     if (/^\@printindex\s+(\w)\w\b/) {
  841.     eval("*ary = *$1index");
  842.     @keys = keys(%ary);
  843.     for $key (@keys) {
  844.         $_ = $key;
  845.         1 while s/<(\w+)>\`(.*)\'<\/\1>/$2/; # remove HTML tags with quotes
  846.         1 while s/<(\w+)>(.*)<\/\1>/$2/; # remove HTML tags
  847.         &unprotect_html;
  848.         &unprotect_texi;
  849.         tr/A-Z/a-z/; # lowercase
  850.         $key2alpha{$key} = $_;
  851.         print "# index $key sorted as $_\n"
  852.         if $key ne $_ && $debug & $DEBUG_INDEX;
  853.     }
  854.     $last_letter = '';
  855.     push(@lines2, "<DIR>\n");
  856.     for (sort(byalpha @keys)) {
  857.         $letter = substr($key2alpha{$_}, 0, 1);
  858.         $letter = substr($key2alpha{$_}, 0, 2) if $letter eq $;;
  859.         if ($letter ne $last_letter) {
  860.         local($_) = $letter;
  861.         &protect_html;
  862.         push(@lines2, "<H2>$_</H2>\n");
  863.         $last_letter = $letter;
  864.         }
  865.         push(@lines2, "<LI>" . &anchor('', $ary{$_}, $_, 1));
  866.     }
  867.     push(@lines2, "</DIR>\n");
  868.     next;
  869.     }
  870.     #
  871.     # simple style substitutions
  872.     #
  873.     $_ = &substitute_style($_);
  874.     #
  875.     # xref
  876.     #
  877.     while (/\@(x|px|info|)ref{($XREFRE)(}?)/o) {
  878.     # note: Texinfo may accept other characters
  879.     ($type, $nodes, $full) = ($1, $2, $3);
  880.     ($before, $after) = ($`, $');
  881.     if (! $full && $after) {
  882.         warn "* Bad xref (no ending } on line): $_";
  883.         $_ = "$before$;0${type}ref\{$nodes$after";
  884.         next; # while xref
  885.     }
  886.     if ($type eq 'x') {
  887.         $type = ($value{'Xrefstring'} || $LDC{'Xrefstring'}).' ';
  888.     } elsif ($type eq 'px') {
  889.         $type = ($value{'xrefstring'} || $LDC{'xrefstring'}).' ';
  890.     } elsif ($type eq 'info') {
  891.         $type = 'See Info';    ### TODO (hm)
  892.     } else {
  893.         $type = '';
  894.     }
  895.     unless ($full) {
  896.         $next = shift(@lines);
  897.         chop($nodes); # remove final newline
  898.         if ($next =~ /\}/) { # splitted on 2 lines
  899.         $nodes .= " $`";
  900.         $after = $';
  901.         } else {
  902.         $nodes .= " $next";
  903.         $next = shift(@lines);
  904.         chop($nodes);
  905.         if ($next =~ /\}/) { # splitted on 3 lines
  906.             $nodes .= " $`";
  907.             $after = $';
  908.         } else {
  909.             warn "* Bad xref (no ending }): $_";
  910.             $_ = "$before$;0xref\{$nodes$after";
  911.             unshift(@lines, $next);
  912.             next; # while xref
  913.         }
  914.         }
  915.     }
  916.     $nodes =~ s/\s+/ /g; # normalize
  917.     @args = split(/\s*,\s*/, $nodes);
  918.     $node = $args[0]; # the node is always the first arg
  919.     $sec = $node2sec{$node};
  920.     if (@args == 5) { # reference to another manual
  921.         $sec = $args[2] || $node;
  922.         $man = $args[4] || $args[3];
  923.         $_ = "${before}${type}".($value{'sectionstring'}||$LC{'sectionstring'})." `$sec' in \@cite{$man}$after"; ### TODO (hm)
  924.     } elsif ($type =~ /Info/) { # inforef
  925.         warn "Wrong number of arguments: $_" unless @args == 3;
  926.         ($nn, $rn, $in) = @args;
  927.         $_ = "${before}${type} file `$in', node `$nn'$after"; ### TODO (hm)
  928.     } elsif ($sec) {
  929.         $href = $node2href{$node};
  930.         $_ = "${before}${type}".($value{'sectionstring'}||$LC{'sectionstring'})." " . &anchor('', $href, $sec) . $after;
  931.     } else {
  932.         warn "Undefined node ($node): $_";
  933.         $_ = "$before$;0xref{$nodes}$after";
  934.     }
  935.     }
  936.     #
  937.     # try to guess bibliography references or glossary terms
  938.     #
  939.     unless (/^<H\d><A NAME=\"SEC\d/) {
  940.     if ($use_bibliography) {
  941.         $done = '';
  942.         while (/$BIBRE/o) {
  943.         ($pre, $what, $post) = ($`, $&, $');
  944.         $href = $bib2href{$what};
  945.         if (defined($href) && $post !~ /^[^<]*<\/A>/) {
  946.             $done .= $pre . &anchor('', $href, $what);
  947.         } else {
  948.             $done .= "$pre$what";
  949.         }
  950.         $_ = $post;
  951.         }
  952.         $_ = $done . $_;
  953.     }
  954.     if ($use_glossary) {
  955.         $done = '';
  956.         while (/\b\w+\b/) {
  957.         ($pre, $what, $post) = ($`, $&, $');
  958.         $entry = $what;
  959.         $entry =~ tr/A-Z/a-z/ unless $entry =~ /^[A-Z\s]+$/;
  960.         $href = $gloss2href{$entry};
  961.         if (defined($href) && $post !~ /^[^<]*<\/A>/) {
  962.             $done .= $pre . &anchor('', $href, $what);
  963.         } else {
  964.             $done .= "$pre$what";
  965.         }
  966.         $_ = $post;
  967.         }
  968.         $_ = $done . $_;
  969.     }
  970.     }
  971.     # otherwise
  972.     push(@lines2, $_);
  973. }
  974. print "# end of pass 2\n" if $verbose;
  975.  
  976. #
  977. # splitted style substitutions
  978. #
  979. while (@lines2) {
  980.     $_ = shift(@lines2);
  981.     #
  982.     # special case (protected sections)
  983.     #
  984.     if (/^$PROTECTTAG/o) {
  985.     push(@lines3, $_);
  986.     next;
  987.     }
  988.     #
  989.     # splitted style substitutions
  990.     #
  991.     $old = '';
  992.     while ($old ne $_) {
  993.         $old = $_;
  994.     if (/\@(\w+)\{/) {
  995.         ($before, $style, $after) = ($`, $1, $');
  996.         if (defined($style_map{$style})) {
  997.         $_ = $after;
  998.         $text = '';
  999.         $after = '';
  1000.         $failed = 1;
  1001.         while (@lines2) {
  1002.             if (/\}/) {
  1003.             $text .= $`;
  1004.             $after = $';
  1005.             $failed = 0;
  1006.             last;
  1007.             } else {
  1008.             $text .= $_;
  1009.             $_ = shift(@lines2);
  1010.             }
  1011.         }
  1012.         if ($failed) {
  1013.             die "* Bad syntax (\@$style) after: $before\n";
  1014.         } else {
  1015.             $text = &apply_style($style, $text);
  1016.             $_ = "$before$text$after";
  1017.         }
  1018.         }
  1019.     }
  1020.     }
  1021.     # otherwise
  1022.     push(@lines3, $_);
  1023. }
  1024. print "# end of pass 3\n" if $verbose;
  1025.  
  1026. #+++############################################################################
  1027. #                                                                              #
  1028. # Pass 4: foot notes, final cleanup                                            #
  1029. #                                                                              #
  1030. #---############################################################################
  1031.  
  1032. @lines4 = ();                # whole document (4th pass)
  1033. @foot_lines = ();            # footnotes
  1034. @doc_lines = ();            # final document
  1035. $end_of_para = 0;            # true if last line is <P>
  1036.  
  1037. while (@lines3) {
  1038.     $_ = shift(@lines3);
  1039.     #
  1040.     # special case (protected sections)
  1041.     #
  1042.     if (/^$PROTECTTAG/o) {
  1043.     push(@doc_lines, $_);
  1044.     $end_of_para = 0;
  1045.     next;
  1046.     }
  1047.     #
  1048.     # footnotes
  1049.     #
  1050.     while (/\@footnote([^\{\s]+)\{/) {
  1051.     ($before, $d, $after) = ($`, $1, $');
  1052.     $_ = $after;
  1053.     $text = '';
  1054.     $after = '';
  1055.     $failed = 1;
  1056.     while (@lines3) {
  1057.         if (/\}/) {
  1058.         $text .= $`;
  1059.         $after = $';
  1060.         $failed = 0;
  1061.         last;
  1062.         } else {
  1063.         $text .= $_;
  1064.         $_ = shift(@lines3);
  1065.         }
  1066.     }
  1067.     if ($failed) {
  1068.         die "* Bad syntax (\@footnote) after: $before\n";
  1069.     } else {
  1070.         $id = 'FOOT' . ++$foot_num;
  1071.         $foot = "($foot_num)";
  1072.         push(@foot_lines, "<H3>" . &anchor($id, "$d#$id", $foot) . "</H3>\n");
  1073.         push(@foot_lines, "$text\n");
  1074.         $_ = $before . &anchor($id, "$docu_foot#$id", $foot) . $after;
  1075.     }
  1076.     }
  1077.     #
  1078.     # remove unnecessary <P>
  1079.     #
  1080.     if ($_ eq "<P>\n") {
  1081.     next if $end_of_para++;
  1082.     } else {
  1083.     $end_of_para = 0;
  1084.     }
  1085.     # otherwise
  1086.     push(@doc_lines, $_);
  1087. }
  1088. print "# end of pass 4\n" if $verbose;
  1089.  
  1090. #+++############################################################################
  1091. #                                                                              #
  1092. # Pass 5: print things                                                         #
  1093. #                                                                              #
  1094. #---############################################################################
  1095.  
  1096. $header = <<EOT;
  1097. <!-- This HTML file has been created by $THISPROG
  1098.      from $docu on $TODAY -->
  1099. EOT
  1100.  
  1101. $_ = &substitute_style($value{'title'} || "Untitled Document");
  1102. &unprotect_texi;
  1103. $title = $_;
  1104.  
  1105. #
  1106. # print TOC
  1107. #
  1108. if (open(FILE, "> $docu_toc")) {
  1109.     print "# creating $docu_toc...\n" if $verbose;
  1110.     &print(FILE, "$header\n");
  1111.     &print(FILE, "<TITLE>$title - Table of Contents</TITLE>\n");
  1112.     &print(FILE,"<H1>$title</H1>\n");
  1113.     if ($value{'subtitle'}) {
  1114.     chop($value{'subtitle'}); # rmv last \n
  1115.     foreach (split(/\n/, $value{'subtitle'})) {
  1116.         $_ = &substitute_style($_);
  1117.         &unprotect_texi;
  1118.         &print(FILE,"<H2>$_</H2>\n");
  1119.     }
  1120.     }
  1121.     if ($value{'author'}) {
  1122.     chop($value{'author'}); # rmv last \n
  1123.     foreach (split(/\n/, $value{'author'})) {
  1124.         $_ = &substitute_style($_);
  1125.         &unprotect_texi;
  1126.         &print(FILE,"<ADDRESS>$_</ADDRESS>\n");
  1127.     }
  1128.     }
  1129.     &print(FILE,"<P>\n");
  1130.     &print_lines(FILE,*toc_lines);
  1131.     close(FILE);
  1132. } else {
  1133.     warn "Can't write to $docu_toc: $!\n";
  1134. }
  1135.  
  1136. #
  1137. # print document
  1138. #
  1139. if ($split_chapter || $split_node) {
  1140.     $doc_num = 0;
  1141.     foreach $section (@sections) {
  1142.     &next_doc;
  1143.     if (open(FILE, "> $docu_doc")) {
  1144.         print "# creating $docu_doc...\n" if $verbose;
  1145.         $prev_doc = &doc_name($doc_num - 1);
  1146.         $prev_doc_title = $sections[$doc_num - 2];
  1147.         $prev_doc = '' if $doc_num == 1;
  1148.         $next_doc = &doc_name($doc_num + 1);
  1149.         $next_doc_title = $sections[$doc_num];
  1150.         $next_doc = '' if $doc_num > $#sections;
  1151.         &print(FILE,"$header\n");
  1152.         &print(FILE,"<TITLE>$title - $section</TITLE>\n\n");
  1153.         $navigation = '';
  1154.         $navigation .= '<< ' . &anchor('', $prev_doc, $prev_doc_title) . "\n" if $prev_doc;
  1155.         $navigation .= '>> ' . &anchor('', $next_doc, $next_doc_title) . "\n" if $next_doc;
  1156.         &print(FILE,$navigation&&"<PRE><I>\n$navigation<HR>\n</I></PRE>\n\n");
  1157.         # find corresponding lines
  1158.             @tmp_lines = ();
  1159.             while (@doc_lines) {
  1160.         $_ = shift(@doc_lines);
  1161.         last if ($_ eq $SPLITTAG);
  1162.         push(@tmp_lines, $_);
  1163.         }
  1164.             &print_lines(FILE,*tmp_lines);
  1165.         &print(FILE,$navigation&&"\n<PRE><I>\n<HR>\n$navigation</I></PRE>\n");
  1166.         close(FILE);
  1167.     } else {
  1168.         warn "Can't write to $docu_doc: $!\n";
  1169.     }
  1170.     }
  1171. } else {
  1172.     if (open(FILE, "> $docu_doc")) {
  1173.     print "# creating $docu_doc...\n" if $verbose;
  1174.     &print(FILE,<<EOT);
  1175. $header
  1176. <TITLE>$title</TITLE>
  1177. <H1>$title</H1>
  1178. EOT
  1179.         &print_lines(FILE,*doc_lines);
  1180.     close(FILE);
  1181.     } else {
  1182.     warn "Can't write to $docu_doc: $!\n";
  1183.     }
  1184. }
  1185.  
  1186. #
  1187. # print footnotes
  1188. #
  1189. if (@foot_lines) {
  1190.     if (open(FILE, "> $docu_foot")) {
  1191.     print "# creating $docu_foot...\n" if $verbose;
  1192.     &print(FILE,<<EOT);
  1193. $header
  1194. <TITLE>$title - Footnotes</TITLE>
  1195. <H1>$title</H1>
  1196. EOT
  1197.         &print_lines(FILE,*foot_lines);
  1198.     close(FILE);
  1199.     } else {
  1200.     warn "Can't write to $docu_foot: $!\n";
  1201.     }
  1202. }
  1203.  
  1204. print "# that's all folks\n" if $verbose;
  1205.  
  1206. #+++############################################################################
  1207. #                                                                              #
  1208. # Low level functions                                                          #
  1209. #                                                                              #
  1210. #---############################################################################
  1211.  
  1212. sub check {
  1213.     local($_, %seen, %context, $before, $match, $after);
  1214.  
  1215.     while (<>) {
  1216.     if (/\@(\*|\.|\:|\@|\{|\})/) {
  1217.         $seen{$&}++;
  1218.         $context{$&} .= "> $_" if $verbose;
  1219.         $_ = "$`XX$'";
  1220.         redo;
  1221.     }
  1222.     if (/\@(\w+)/) {
  1223.         ($before, $match, $after) = ($`, $&, $');
  1224.         if ($before =~ /\b[\w-]+$/ && $after =~ /^[\w-.]*\b/) { # e-mail address
  1225.         $seen{'e-mail address'}++;
  1226.         $context{'e-mail address'} .= "> $_" if $verbose;
  1227.         } else {
  1228.         $seen{$match}++;
  1229.         $context{$match} .= "> $_" if $verbose;
  1230.         }
  1231.         $match =~ s/^\@/X/;
  1232.         $_ = "$before$match$after";
  1233.         redo;
  1234.     }
  1235.     }
  1236.     
  1237.     for (sort(keys(%seen))) {
  1238.     if ($verbose) {
  1239.         print "$_\n";
  1240.         print $context{$_};
  1241.     } else {
  1242.         print "$_ ($seen{$_})\n";
  1243.     }
  1244.     }
  1245. }
  1246.  
  1247. sub open {
  1248.     local($name) = @_;
  1249.  
  1250.     ++$fh_name;
  1251.     if (open($fh_name, $name)) {
  1252.     unshift(@fhs, $fh_name);
  1253.     } else {
  1254.     warn "Can't read file $name: $!\n";
  1255.     }
  1256. }
  1257.  
  1258. sub init_input {
  1259.     @fhs = ();            # hold the file handles to read
  1260.     @input_spool = ();        # spooled lines to read
  1261.     $fh_name = 'FH000';
  1262.     &open($docu);
  1263. }
  1264.  
  1265. sub next_line {
  1266.     local($fh, $line);
  1267.  
  1268.     if (@input_spool) {
  1269.     $line = shift(@input_spool);
  1270.     return($line);
  1271.     }
  1272.     while (@fhs) {
  1273.     $fh = $fhs[0];
  1274.     $line = <$fh>;
  1275.     return($line) if $line;
  1276.     close($fh);
  1277.     shift(@fhs);
  1278.     }
  1279.     return(undef);
  1280. }
  1281.  
  1282. # used in pass 1, use &next_line
  1283. sub skip_until {
  1284.     local($tag) = @_;
  1285.     local($_);
  1286.  
  1287.     while ($_ = &next_line) {
  1288.     return if /^\@end\s+$tag\s*$/;
  1289.     }
  1290.     die "* Failed to find '$tag' after: " . $lines[$#lines];
  1291. }
  1292.  
  1293. sub menu_entry {
  1294.     local($entry, $node, $descr) = @_;
  1295.     local($href);
  1296.  
  1297.     $href = $node2href{$node};
  1298.     if ($href) {
  1299.     $descr =~ s/^\s+//;
  1300.     $descr = ": $descr" if $descr;
  1301.     push(@lines2, "<LI>" . &anchor('', $href, $entry) . "$descr\n");
  1302.     } else {
  1303.     warn "Undefined node ($node): $_";
  1304.     }
  1305. }
  1306.  
  1307. sub do_ctrl { "^$_[0]" }
  1308.  
  1309. sub do_sc { "\U$_[0]\E" }
  1310.  
  1311. sub apply_style {
  1312.     local($texi_style, $text) = @_;
  1313.     local($style);
  1314.  
  1315.     $style = $style_map{$texi_style};
  1316.     if (defined($style)) { # known style
  1317.     if ($style =~ /^\"/) { # add quotes
  1318.         $style = $';
  1319.         $text = "\`$text\'";
  1320.     }
  1321.     if ($style =~ /^\&/) { # custom
  1322.         $style = $';
  1323.         $text = &$style($text);
  1324.     } elsif ($style) { # good style
  1325.         $text = "<$style>$text</$style>";
  1326.     } else { # no style
  1327.     }
  1328.     } else { # unknown style
  1329.     $text = undef;
  1330.     }
  1331.     return($text);
  1332. }
  1333.  
  1334. sub substitute_style {
  1335.     local($_) = @_;
  1336.     local($changed, $done, $style, $text);
  1337.  
  1338.     $changed = 1;
  1339.     while ($changed) {
  1340.     $changed = 0;
  1341.     $done = '';
  1342.     while (/\@(\w+){([^\{\}]+)}/) {
  1343.         $text = &apply_style($1, $2);
  1344.         if ($text) {
  1345.         $_ = "$`$text$'";
  1346.         $changed = 1;
  1347.         } else {
  1348.         $done .= "$`\@$1";
  1349.         $_ = "{$2}$'";
  1350.         }
  1351.     }
  1352.         $_ = $done . $_;
  1353.     }
  1354.     return($_);
  1355. }
  1356.  
  1357. sub anchor {
  1358.     local($name, $href, $text, $newline) = @_;
  1359.     local($result);
  1360.  
  1361.     $result = "<A";
  1362.     $result .= " NAME=\"$name\"" if $name;
  1363.     $result .= " HREF=\"$href\"" if $href;
  1364.     $result .= ">$text</A>";
  1365.     $result .= "\n" if $newline;
  1366.     return($result);
  1367. }
  1368.  
  1369. sub pretty_date {
  1370.     local(@MoY, $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
  1371.  
  1372.     @MoY = ('January', 'Febuary', 'March', 'April', 'May', 'June',
  1373.         'July', 'August', 'September', 'October', 'November', 'December');
  1374.     ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
  1375.     $year += ($year < 70) ? 2000 : 1900;
  1376.     return("$mday $MoY[$mon] $year");
  1377. }
  1378.  
  1379. sub doc_name {
  1380.     local($num) = @_;
  1381.  
  1382.     return("${docu_name}_$num.html");
  1383. }
  1384.  
  1385. sub next_doc {
  1386.     $docu_doc = &doc_name(++$doc_num);
  1387. }
  1388.  
  1389. sub print_lines {
  1390.     local($fh,*lines) = @_;
  1391.     local($_);
  1392.  
  1393.     while (@lines) {
  1394.     $_ = shift(@lines);
  1395.     if (/^$PROTECTTAG/o) {
  1396.         $_ = $tag2pro{$_};
  1397.     } else {
  1398.         &unprotect_texi;
  1399.     }
  1400.     &print($fh,$_);
  1401.     }
  1402. }
  1403.  
  1404. sub print
  1405. {
  1406.     local($fh)=shift(@_);
  1407.  
  1408.     grep(s/[\x80-\xff]/$iso{$&}||sprintf("&#%d;",unpack("C",$&))/eig,@_);
  1409.     print $fh @_;
  1410. }
  1411.  
  1412. sub protect_texi {
  1413.     # protect @ { } ` '
  1414.     s/\@\@/$;0/go && study;
  1415.     s/\@\{/$;1/go && study;
  1416.     s/\@\}/$;2/go && study;
  1417.     s/\@\`/$;3/go && study;
  1418.     s/\@\'/$;4/go && study;
  1419. }
  1420.  
  1421. sub protect_html {
  1422.     # protect & < >
  1423.     s/\&/\&\#38;/g && study;
  1424.     s/\</\&\#60;/g && study;
  1425.     s/\>/\&\#62;/g && study;
  1426.     s/\&\#60;\/A\&\#62;/<\/A>/g && study; # assume </A> is HTML
  1427.     s/\&\#60;A ([^\&]+)\&\#62;/<A $1>/g && study; # assume <A [^&]+> is HTML
  1428. }
  1429.  
  1430. # we should use this:
  1431. #
  1432. #  sub unprotect_texi {
  1433. #      s/$;0/\@/go && study;
  1434. #      s/$;1/\{/go && study;
  1435. #      s/$;2/\}/go && study;
  1436. #      s/$;3/\`/go && study;
  1437. #      s/$;4/\'/go && study;
  1438. #  }
  1439. #  
  1440. #  sub unprotect_html {
  1441. #      s/\&\#38;/\&/g && study;
  1442. #      s/\&\#60;/\</g && study;
  1443. #      s/\&\#62;/\>/g && study;
  1444. #  }
  1445. #
  1446. # but Perl 4.036 bugs on it (Perl 5 is OK)
  1447.  
  1448. sub unprotect_texi {
  1449.     s/$;0/\@/go;
  1450.     s/$;1/\{/go;
  1451.     s/$;2/\}/go;
  1452.     s/$;3/\`/go;
  1453.     s/$;4/\'/go;
  1454. }
  1455.  
  1456. sub unprotect_html {
  1457.     s/\&\#38;/\&/g;
  1458.     s/\&\#60;/\</g;
  1459.     s/\&\#62;/\>/g;
  1460. }
  1461.  
  1462. sub byalpha {
  1463.     $key2alpha{$a} cmp $key2alpha{$b};
  1464. }
  1465.  
  1466. ##############################################################################
  1467.  
  1468.     # These next few lines are legal in both Perl and nroff.
  1469.  
  1470. .00;            # finish .ig
  1471.  
  1472. 'di            \" finish diversion--previous line must be blank
  1473. .nr nl 0-1        \" fake up transition to first page again
  1474. .nr % 0            \" start at page 1
  1475. '; __END__ ############# From here on it's a standard manual page ############
  1476. .TH TEXI2HTML 1 "04/21/94"
  1477. .AT 3
  1478. .SH NAME
  1479. texi2html \- a Texinfo to HTML converter
  1480. .SH SYNOPSIS
  1481. .B texi2html [options] file
  1482. .PP
  1483. .B texi2html -check [-verbose] files
  1484. .SH DESCRIPTION
  1485. .I Texi2html
  1486. converts the given Texinfo file to a set of HTML files. It tries to handle
  1487. most of Texinfo commands. It creates hypertext links for cross-references,
  1488. footnotes...
  1489. .PP
  1490. It also tries to add links from a reference to its corresponding entry in the
  1491. bibliography (if any). It may also handle a glossary (see the
  1492. .B \-glossary
  1493. option).
  1494. .PP
  1495. .I Texi2html
  1496. creates several files depending on the contents of the Texinfo file and on
  1497. the chosen options (see FILES).
  1498. .PP
  1499. The HTML files created by
  1500. .I texi2html
  1501. are closer to TeX than to Info, what's why
  1502. .I texi2html
  1503. convert @iftex sections and not @ifinfo ones.
  1504. .SH OPTIONS
  1505. .TP 12
  1506. .B \-check
  1507. Check the given file and give the list of all things that may be Texinfo commands.
  1508. This may be used to check the output of
  1509. .I texi2html
  1510. to find the Texinfo commands that have been left in the HTML file.
  1511. .TP
  1512. .B \-glossary
  1513. Use the section named 'Glossary' to build a list of terms and put links in the HTML
  1514. document from each term toward its definition.
  1515. .TP
  1516. .B \-invisible \fIname\fP
  1517. Use \fIname\fP to create invisible destination anchors for index links. This is a workaround
  1518. for a known bug of many WWW browsers, including xmosaic.
  1519. .TP
  1520. .B \-menu
  1521. Show the Texinfo menus; by default they are ignored.
  1522. .TP
  1523. .B \-split_chapter
  1524. Split the output into several HTML files (one per main section:
  1525. chapter, appendix...).
  1526. .TP
  1527. .B \-split_node
  1528. Split the output into several HTML files (one per node).
  1529. .TP
  1530. .B \-usage
  1531. Print usage instructions, listing the current available command-line options.
  1532. .TP
  1533. .B \-verbose
  1534. Give a verbose output. Can be used with the
  1535. .B \-check
  1536. option.
  1537. .PP
  1538. .SH FILES
  1539. By default
  1540. .I texi2html
  1541. creates the following files (foo being the name of the Texinfo file):
  1542. .TP 16
  1543. .B foo_toc.html
  1544. The table of contents.
  1545. .TP
  1546. .B foo.html
  1547. The document's contents.
  1548. .TP
  1549. .B foo_foot.html
  1550. The footnotes (if any).
  1551. .PP
  1552. When used with the
  1553. .B \-split
  1554. option, it creates several files (one per chapter or node), named
  1555. .B foo_n.html
  1556. (n being the indice of the chapter or node), instead of the single
  1557. .B foo.html
  1558. file.
  1559. .SH VARIABLES
  1560. .I texi2html
  1561. predefines the following variables: \fBhtml\fP, \fBtexi2html\fP.
  1562. .SH ADDITIONAL COMMANDS
  1563. .I texi2html
  1564. implements the following non-Texinfo commands:
  1565. .TP 16
  1566. .B @ifhtml
  1567. This indicates the start of an HTML section, this section will passed through
  1568. without any modofication.
  1569. .TP
  1570. .B @end ifhtml
  1571. This indcates the end of an HTML section.
  1572. .SH VERSION
  1573. This is \fItexi2html\fP version 1.29, 04/21/94.
  1574. .SH AUTHOR
  1575. Lionel Cons, CERN CN/DCI/UWS, Lionel.Cons@cern.ch
  1576. .SH COPYRIGHT
  1577. This program is the intellectual property of the European
  1578. Laboratory for Particle Physics (known as CERN). No guarantee whatsoever is
  1579. provided by CERN. No liability whatsoever is accepted for any loss or damage
  1580. of any kind resulting from any defect or inaccuracy in this information or
  1581. code.
  1582. .PP
  1583. CERN, 1211 Geneva 23, Switzerland
  1584. .SH "SEE ALSO"
  1585. GNU Texinfo Documentation Format,
  1586. HyperText Markup Language (HTML),
  1587. World Wide Web (WWW).
  1588. .SH BUGS
  1589. This program does not understand all Texinfo commands (yet).
  1590. .ex
  1591.